<template>
  <div class="home">
    <el-container style="height: 100vh">
      <el-header class="header">{{ this.$config.web_name }}</el-header>
      <el-main>
        <div style="width: 90%; margin: 20px auto">
          <el-tabs style="background-color: #fff" v-model="activeName" class="tabs" type="border-card"
            @tab-click="handleClick">
            <el-tab-pane label="移动应用恶意代码检测" name="apk">
              <div class="btn_group">
                <el-upload :limit="this.$config.upload_limit" :on-exceed="handleExceed" class="margin_right" action=""
                  ref="upload0" multiple :http-request="upload" :show-file-list="false"
                  :before-upload="format_check_apk" accept="application/vnd.android.package-archive">
                  <el-button type="primary">上传APK文件</el-button>
                  <template #tip>
                    <div class="upload_tip">
                      上传文件≤20个/次
                      <!-- 上传文件≤20个/次，<br>单个文件≤50Mb -->
                    </div>
                  </template></el-upload>
                <el-upload :limit="this.$config.upload_limit" :on-exceed="handleExceed" class="margin_right" action=""
                  :before-upload="format_check_json" ref="upload1" multiple :http-request="upload"
                  :show-file-list="false" accept="application/json">
                  <el-button type="primary">上传特征文件</el-button></el-upload>
                <el-upload :limit="this.$config.upload_limit" :on-exceed="handleExceed" class="margin_right" action=""
                  :before-upload="format_check_apk" ref="upload2" multiple :http-request="upload_mal"
                  :show-file-list="false" accept="application/vnd.android.package-archive">
                  <el-button type="primary">上传恶意文件</el-button></el-upload>
                <el-upload :limit="this.$config.upload_limit" :on-exceed="handleExceed" class="margin_right" action=""
                  :before-upload="format_check_apk" ref="upload3" multiple :http-request="upload_ben"
                  :show-file-list="false" accept="application/vnd.android.package-archive">
                  <el-button type="primary">上传良性文件</el-button></el-upload>
                <el-button class="margin_right" @click="refresh(android_list)">刷新</el-button>
                <div class="margin_right line_height_32 float_right">正确率：{{ correct_ratio_android }}%</div>
                <!-- <div class="btn_group"><el-button type="" @click="android_reset">重置</el-button></div> -->
              </div>
              <el-divider style="margin-top: 12px;"></el-divider>
              <div class="list_box">
                <div v-for="item in android_list" :key="item.id">
                  <Mycard :show_download="true" :info="item"
                    @download="download_features('android', item.filename, item.id)">
                  </Mycard>
                </div>
                <el-empty v-if="android_list.length == 0" description="点击按钮上传文件" />
              </div>
            </el-tab-pane>
            <el-tab-pane label="桌面应用勒索软件检测" name="pe">
              <div class="btn_group">
                <el-upload disabled><el-switch v-model="pe_switch" active-text="PE" inactive-text="ELF"
                    style="--el-switch-off-color: #13ce66;margin:0 10px" /><template #tip>
                    <div class="upload_tip">
                      上传文件≤20个/次
                      <!-- 上传文件≤20个/次，<br>单个文件≤50Mb -->
                    </div>
                  </template></el-upload>
                <el-upload :limit="this.$config.upload_limit" :on-exceed="handleExceed" class="margin_right" action=""
                  ref="upload4" multiple :http-request="upload" :show-file-list="false"
                  :before-upload="pe_switch ? format_check_pe : format_check_elf" accept="application/x-msdownload">
                  <el-button v-if="pe_switch" type="primary">上传PE文件</el-button>
                  <el-button v-else type="primary">上传ELF文件</el-button>
                </el-upload>
                <el-upload :limit="this.$config.upload_limit" :on-exceed="handleExceed" class="margin_right" action=""
                  :before-upload="format_check_json" ref="upload5" multiple :http-request="upload"
                  :show-file-list="false" accept="application/json">
                  <el-button type="primary">上传特征文件</el-button></el-upload>
                <el-upload :limit="this.$config.upload_limit" :on-exceed="handleExceed" class="margin_right" action=""
                  ref="upload6" multiple :http-request="upload_mal" :show-file-list="false"
                  :before-upload="pe_switch ? format_check_pe : format_check_elf" accept="application/x-msdownload">
                  <el-button type="primary">上传恶意文件</el-button></el-upload>
                <el-upload :limit="this.$config.upload_limit" :on-exceed="handleExceed" class="margin_right" action=""
                  ref="upload7" multiple :http-request="upload_ben" :show-file-list="false"
                  :before-upload="pe_switch ? format_check_pe : format_check_elf" accept="application/x-msdownload">
                  <el-button type="primary">上传良性文件</el-button></el-upload>
                <el-button class="margin_right" @click="refresh(pe_list)">刷新</el-button>
                <div class="margin_right line_height_32 float_right">正确率：{{ correct_ratio_pe }}%</div>
              </div>
              <el-divider style="margin-top: 2px;"></el-divider>
              <div class="list_box">
                <div v-for="item in pe_list" :key="item.id">
                  <Mycard :show_download="true" :info="item"
                    @download="download_features('pe', item.filename, item.id)">
                  </Mycard>
                </div>
                <el-empty v-if="pe_list.length == 0" description="点击按钮上传文件" />
              </div>
            </el-tab-pane>
          </el-tabs>
        </div>
      </el-main>
    </el-container>
  </div>
</template>

<script>
// @ is an alias to /src
import { reactive, ref } from "vue";
import Mycard from "@/components/Mycard.vue";

export default {
  components: {
    Mycard,
  },
  setup() {
    let android_list = reactive([]);
    let pe_list = reactive([]);
    let android_running = reactive([]);
    let pe_running = reactive([]);
    return {
      activeName: ref("apk"),
      pe_switch: ref(true),
      current_card: "apk",
      android_list,
      pe_list,
      android_running,
      pe_running,
      android_interval: false,
      pe_interval: false,
      correct_ratio_android: ref(" -- "),
      correct_ratio_pe: ref(" -- "),
    };
  },
  methods: {
    refresh(list) {
      while (list.length > 0) {
        list.pop(); // 删除数组最后一个元素
      }
      this.update_correct_ratio_android()
      this.update_correct_ratio_pe()
    },
    async format_check_apk(file) {
      if (file.type != "application/vnd.android.package-archive") {
        this.$message.error("上传文件格式必须为APK!")
        return false
      }
      let type = await this.file_type(file)
      if (type != "apk") {
        this.$message.error("上传文件格式必须为APK!")
        return false
      }
      return true
    },
    format_check_json(file) {
      if (file.type != "application/json") {
        this.$message.error("上传文件格式必须为JSON!")
        return false
      }
      return true
    },
    async format_check_pe(file) {
      if (file.type != "application/x-msdownload") {
        this.$message.error("上传文件格式必须为PE!")
        return false
      }
      let type = await this.file_type(file)
      if (type != "pe") {
        this.$message.error("上传文件格式必须为PE!")
        return false
      }
      return true
    },
    async format_check_elf(file) {
      let type = await this.file_type(file)
      if (type != "elf") {
        this.$message.error("上传文件格式必须为ELF!")
        return false
      }
      return true
    },
    clear_files() {
      this.$refs.upload0.clearFiles()
      this.$refs.upload1.clearFiles()
      this.$refs.upload2.clearFiles()
      this.$refs.upload4.clearFiles()
      this.$refs.upload5.clearFiles()
      this.$refs.upload6.clearFiles()
      this.$refs.upload7.clearFiles()
    },
    handleExceed() {
      this.$message.error("上传文件个数超过限制(≤" + this.$config.upload_limit + ")")
    },
    android_reset() {
      this.android_list.length = 0
      this.android_running.length = 0
      this.android_list = reactive([])
      this.android_running = reactive([])
      this.android_interval = false
    },
    download_features(type, filename, key) {
      console.log(type, filename)
      let url = "/download/" + type + "/" + key
      this.download(url, filename)
    },
    download(url, filename) {
      this.$http.get(url, { responseType: 'blob' }).then(res => {
        const fileURL = window.URL.createObjectURL(new Blob([res.data], { type: 'application/json' }))
        const link = document.createElement('a')
        link.href = fileURL
        link.download = filename.replace(/\.[^/.]+$/, "") + ".json"
        link.click()
      })
    },
    handleClick(tab, event) {
      console.log(tab.props.name);
      this.current_card = tab.props.name;
    },
    upload_mal(data) {
      this.upload(data, "malignance")
    },
    upload_ben(data) {
      this.upload(data, "benign")
    },
    upload(data, label = null) {
      console.log("upload", label);
      // console.log(data);
      const file = data.file;

      const key = this.generateRandomString();
      let flag;
      console.log(file.type)
      let type = "raw"
      let f_type
      if (file.type == "application/json") {
        type = "feature";
        flag = this.current_card;
        f_type = "json"
      }
      else {
        if (this.activeName == "apk") {
          f_type = "apk"
          flag = f_type
        } else {
          f_type = this.pe_switch ? "pe" : "elf" //await this.file_type(file)
          flag = f_type
        }
      }
      console.log(flag)
      const formData = new FormData();
      formData.append("file", file);
      formData.append("key", key);
      formData.append("flag", flag);
      formData.append("type", type)
      const fileinfo = {
        id: key,
        filename: file.name,
        filesize: file.size,
        filetype: f_type,
        state: "uploading",
        label: label,
        progress: 0,
        upload_time: new Date().getTime(),
      };
      let current_list = [];
      if (this.current_card == "apk") {
        this.android_list.unshift(fileinfo);
        current_list = this.android_list;
      }
      if (this.current_card == "pe") {
        this.pe_list.unshift(fileinfo);
        current_list = this.pe_list;
      }
      console.log(key);
      this.$http
        .post("/file/upload", formData, {
          onUploadProgress: (progressEvent) => {
            const progress = Math.round(
              (progressEvent.loaded / progressEvent.total) * 100
            );
            current_list.forEach((e) => {
              if (e.id == key) {
                e.progress = progress;
              }
            });
          },
        })
        .then((res) => {
          console.log(res); //处理
          current_list.forEach((e) => {
            if (e.id == key) {
              e.state = "running";
            }
          });
          if (flag == "apk") {
            this.android_running.push(key);
          } else if (flag == "pe") {
            this.pe_running.push(key);
          } else {
            this.pe_running.push(key)
          }
          this.clear_files()
          this.update();
        });
    },
    update() {
      if (this.android_interval == false && this.android_running.length != 0) {
        setTimeout(() => this.update_android_state(), this.$config.update_interval);
        this.android_interval = true;
      }
      if (this.pe_interval == false && this.pe_running.length != 0) {
        setTimeout(() => this.update_pe_state(), this.$config.update_interval);
        this.pe_interval = true;
      }
    },
    upload_json(data) {
      const file = data.file;
    },
    update_android_state() {
      this.$http
        .post("/state/android", { keylist: this.android_running })
        .then((res) => {
          console.log(res);
          const state = new Map(Object.entries(res.data.data.state));
          console.log(state);
          for (let [key, val] of state) {
            if (val != "running") {
              let index = this.android_running.indexOf(key);
              if (index != -1) this.android_running.splice(index, 1);
              this.android_list.forEach((e) => {
                if (e.id == key) {
                  e.state = val;
                }
              });
            }
          }
          this.update_correct_ratio_android()
          this.android_interval = false;
          this.update();
        });
    },
    update_pe_state() {
      this.$http
        .post("/state/pe", { keylist: this.pe_running })
        .then((res) => {
          console.log(res);
          const state = new Map(Object.entries(res.data.data.state));
          for (let [key, val] of state) {
            if (val != "running") {
              let index = this.pe_running.indexOf(key);
              this.pe_running.splice(index, 1);
              this.pe_list.forEach((e) => {
                if (e.id == key) {
                  e.state = val;
                }
              });
            }
          }
          this.update_correct_ratio_pe()
          this.pe_interval = false;
          this.update();
        });
    },
    generateRandomString() {
      let RANDOMKEY = 0;
      let str = "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(
        /[xy]/g,
        function (c) {
          var r = (Math.random() * 16) | 0,
            v = c == "x" ? r : (r & 0x3) | 0x8;
          return v.toString(16) + ++RANDOMKEY;
        }
      );
      return str.substring(str.length - 45);
    },
    update_correct_ratio_pe() {
      console.log("update correct_ratio_pe")
      let total = 0
      let right = 0
      this.pe_list.forEach((e) => {
        if (e.label && ["malignance", "benign"].indexOf(e.state) > -1) {
          total += 1
          if (e.state == e.label) {
            right += 1
          }
        }
      });
      // console.log(total)
      let value
      if (total == 0) value = " -- "
      else value = (right * 100 / total).toFixed(2)
      // console.log(value)
      this.correct_ratio_pe = value
    },
    update_correct_ratio_android() {
      console.log("update correct_ratio_android")
      let total = 0
      let right = 0
      this.android_list.forEach((e) => {
        if (e.label && ["malignance", "benign"].indexOf(e.state) > -1) {
          total += 1
          if (e.state == e.label) {
            right += 1
          }
        }
      });
      // console.log(total)
      let value
      if (total == 0) value = " -- "
      else value = (right * 100 / total).toFixed(2)
      // console.log(value)
      this.correct_ratio_android = value
      // console.log(this.correct_ratio_android)
    },
    file_type(file) {
      return new Promise((resovle, reject) => {
        console.log("filetype")
        const reader = new FileReader()
        reader.onload = (e) => {
          const arrayBuffer = reader.result;
          const magicNumber = this.getMagicNumber(arrayBuffer);
          if (magicNumber) {
            console.log('Magic Number:', magicNumber);

            if (magicNumber === '504b0304') {
              console.log('File format: APK');
              resovle("apk")
            } else if (magicNumber === '4d5a9000') {
              console.log('File format: EXE');
              resovle("pe")
            } else if (magicNumber === '7f454c46') {
              console.log('File format: ELF');
              resovle('elf')
            } else {
              console.log('Unknown file format');
              resovle("other")
            }
          }
        }
        reader.readAsArrayBuffer(file);
      })
    },
    getMagicNumber(arrayBuffer) {
      const uint8Array = new Uint8Array(arrayBuffer);
      let magicNumber = '';

      for (let i = 0; i < 4; i++) {
        const hex = uint8Array[i].toString(16).padStart(2, '0');
        magicNumber += hex;
      }

      return magicNumber;
    },
  },

};
</script>

<style lang="less" scoped>
.header {
  height: 60px;
  line-height: 60px;
  font-size: 40px;
  font-weight: bold;
  text-align: left;
  background-color: #fff;
  box-shadow: 0px 5px 5px -5px #888888;
}

.list_box {
  min-height: 300px;
}

.margin_right {
  margin-right: 25px;
}

.btn_group {
  text-align: left;
  display: flex
}

.line_height_32 {
  height: 32px;
  line-height: 32px;
  font-weight: bold;
  color: #888888;

}

.float_right {
  margin-left: auto;
  min-width: 100px;
}

.upload_tip {
  font-size: 10px;
  color: #888888;
}
</style>
